from services.stock_margin_service import StockMarginService
from services.sectorwise_market_factors_service import SectorwiseMarketFactorsService
from services.stock_index_service import StockIndexService
from services.hk_index_service import HkIndexService
from services.global_index_service import GlobalIndexService
from services.options_service import OptionsService
from config.settings import settings
from loguru import logger
import pandas as pd
from datetime import datetime, timedelta
from util import get_latest_trading_date
from functools import lru_cache


class DataViewPluginService:
    def __init__(self):
        self.stock_margin_service = StockMarginService()
        self.sectorwise_market_factors_service = SectorwiseMarketFactorsService()
        self.stock_index_service = StockIndexService()
        self.hk_index_service = HkIndexService()
        self.global_index_service = GlobalIndexService()
        self.options_service = OptionsService()
        # Track last trading date to proactively clear caches across days
        self._last_cache_trading_date = None

    def get_daily_history_with_cache(self, codes, start_date: str, end_date: str):
        """
        获取日线历史数据（带LRU缓存）。

        使用 LRU 缓存机制，当参数不变时复用缓存；
        分钟级数据不在此方法缓存范围内（仍按需实时获取）。

        Args:
            codes: 代码列表或可迭代对象
            start_date: 开始日期 YYYY-MM-DD
            end_date: 结束日期 YYYY-MM-DD（通常为最新交易日）

        Returns:
            pd.DataFrame | None
        """
        try:
            # Proactively clear cache when trading day changes
            try:
                latest_trading_date = get_latest_trading_date()
                if latest_trading_date and latest_trading_date != self._last_cache_trading_date:
                    self.clear_daily_history_cache()
                    self._last_cache_trading_date = latest_trading_date
            except Exception:
                # Do not fail the call if trading date lookup fails
                pass
            codes_list = list(codes)
            codes_key = tuple(sorted([str(c) for c in codes_list]))
            return self._get_cached_daily_history(codes_key, start_date, end_date)
        except Exception as e:
            logger.exception(f"获取日线历史数据（带缓存）失败: {e}")
            return None

    @lru_cache(maxsize=16)
    def _get_cached_daily_history(self, codes_tuple: tuple, start_date: str, end_date: str):
        """使用LRU缓存获取日线历史数据"""
        logger.info("日线历史数据缓存未命中，从数据源获取")
        codes_list = list(codes_tuple)
        df = self.stock_index_service.get_history_data(
            codes=codes_list,
            start_date=start_date,
            end_date=end_date
        )
        return df.copy() if df is not None and not df.empty else df

    def clear_daily_history_cache(self):
        """清除日线历史数据缓存"""
        logger.info("清除日线历史数据LRU缓存")
        self._get_cached_daily_history.cache_clear()

    def get_margin_trading_data(self, start_date, end_date):
        """获取融资融券数据"""
        try:
            # 分别获取SSE和SZSE数据
            sse_df = self.stock_margin_service.get_margin_balance(
                exchange="sse",
                start_date=start_date, 
                end_date=end_date
            )
            
            szse_df = self.stock_margin_service.get_margin_balance(
                exchange="szse",
                start_date=start_date, 
                end_date=end_date
            )
            
            # 合并数据
            df_list = []
            if sse_df is not None and not sse_df.empty:
                df_list.append(sse_df)
            if szse_df is not None and not szse_df.empty:
                df_list.append(szse_df)
            
            df = pd.concat(df_list, ignore_index=True) if df_list else pd.DataFrame()
            
            if df is not None and not df.empty:
                # Convert date column to datetime objects
                df['date'] = pd.to_datetime(df['date'])
            
            return df
            
        except Exception as e:
            logger.exception(f"获取融资融券数据时出错: {e}")
            return pd.DataFrame()

    def process_exchange_data(self, df, exchange, field):
        """处理交易所数据并返回按日期汇总的结果"""
        if exchange == 'all':
            # 全市场数据：按日期汇总所有交易所的数据
            daily_data = df.groupby('date')[field].sum().reset_index()
        else:
            # 单个交易所数据
            exchange_df = df[df['exchange_name'] == exchange]
            if exchange_df.empty:
                return pd.DataFrame()
            daily_data = exchange_df.groupby('date')[field].sum().reset_index()
        
        # 排序
        daily_data = daily_data.sort_values('date')
        
        # 数据有效性检查和最后一天异常数据过滤
        if len(daily_data) >= 2:
            # 检查最后一天是否相比前一天下降超过30%
            last_value = daily_data.iloc[-1][field]
            second_last_value = daily_data.iloc[-2][field]
            
            if second_last_value > 0:  # 避免除零错误
                drop_percentage = (second_last_value - last_value) / second_last_value
                if drop_percentage > 0.3:  # 下降超过30%
                    logger.warning(f"{exchange} {field} 最后一天数据下降{drop_percentage:.1%}，可能数据不全，已过滤")
                    daily_data = daily_data.iloc[:-1]  # 移除最后一天数据
        
        return daily_data

    def get_total_market_volume(self, start_date_str, end_date_str):
        """获取全市场成交额数据并汇总各板块"""
        try:
            # 将YYYYMMDD格式转换为YYYY-MM-DD格式
            start_date = f"{start_date_str[:4]}-{start_date_str[4:6]}-{start_date_str[6:8]}"
            end_date = f"{end_date_str[:4]}-{end_date_str[4:6]}-{end_date_str[6:8]}"
            
            # 获取所有板块的市场数据
            market_data = self.sectorwise_market_factors_service.get_sectorwise_market_factors(
                start_date=start_date,
                end_date=end_date
            )
            
            if market_data is None or market_data.empty:
                logger.warning("未获取到市场成交额数据")
                return pd.DataFrame()
            
            # 按日期汇总所有板块的成交额
            total_volume_df = market_data.groupby('date')['amount'].sum().reset_index()
            total_volume_df.columns = ['date', 'total_market_amount']
            
            # 确保日期格式一致
            total_volume_df['date'] = pd.to_datetime(total_volume_df['date'])
            
            return total_volume_df
            
        except Exception as e:
            logger.error(f"获取全市场成交额数据失败: {e}")
            return pd.DataFrame()

    def calculate_financing_ratio(self, margin_df, market_volume_df):
        """计算融资买入额占全市场成交额的比例"""
        try:
            # 按日期汇总融资买入额
            financing_daily = margin_df.groupby('date')['financing_buy_amount'].sum().reset_index()
            
            # 对融资买入额数据进行最后一天异常数据检查
            if len(financing_daily) >= 2:
                financing_daily = financing_daily.sort_values('date')
                last_value = financing_daily.iloc[-1]['financing_buy_amount']
                second_last_value = financing_daily.iloc[-2]['financing_buy_amount']
                
                if second_last_value > 0:  # 避免除零错误
                    drop_percentage = (second_last_value - last_value) / second_last_value
                    if drop_percentage > 0.3:  # 下降超过30%
                        logger.warning(f"融资买入额最后一天数据下降{drop_percentage:.1%}，可能数据不全，已过滤")
                        financing_daily = financing_daily.iloc[:-1]  # 移除最后一天数据
            
            # 合并融资数据和市场成交额数据
            merged_df = pd.merge(financing_daily, market_volume_df, on='date', how='inner')
            
            # 使用向量化计算比例
            merged_df['financing_ratio'] = (merged_df['financing_buy_amount'] / merged_df['total_market_amount']) * 100
            
            # 过滤异常值（比例大于100%或小于0%的情况）
            merged_df = merged_df[
                (merged_df['financing_ratio'] >= 0) & 
                (merged_df['financing_ratio'] <= 100)
            ]
            
            return merged_df[['date', 'financing_ratio']].sort_values('date')
            
        except Exception as e:
            logger.error(f"计算融资买入比例失败: {e}")
            return pd.DataFrame()

    def get_financing_balance_for_percentile(self, margin_df):
        """获取用于百分位数计算的融资余额数据（全市场汇总）"""
        try:
            # 按日期汇总全市场融资余额
            balance_daily = margin_df.groupby('date')['financing_balance'].sum().reset_index()
            
            # 对融资余额数据进行最后一天异常数据检查
            if len(balance_daily) >= 2:
                balance_daily = balance_daily.sort_values('date')
                last_value = balance_daily.iloc[-1]['financing_balance']
                second_last_value = balance_daily.iloc[-2]['financing_balance']
                
                if second_last_value > 0:  # 避免除零错误
                    drop_percentage = (second_last_value - last_value) / second_last_value
                    if drop_percentage > 0.3:  # 下降超过30%
                        logger.warning(f"融资余额最后一天数据下降{drop_percentage:.1%}，可能数据不全，已过滤")
                        balance_daily = balance_daily.iloc[:-1]  # 移除最后一天数据
            
            return balance_daily.sort_values('date')
            
        except Exception as e:
            logger.error(f"获取融资余额数据失败: {e}")
            return pd.DataFrame()

    def get_index_configs(self):
        """从配置文件获取指数配置"""
        try:
            # 从 settings.py 获取配置
            configs = settings.index_code_config
            # 将字典转换为 (code, name) 元组列表
            return list(configs.items())
        except Exception as e:
            logger.error(f"获取指数配置失败: {e}")
            return []

    def calculate_periods_return(self, code):
        """计算各个时间段的涨跌幅"""
        periods_data = {}
        
        try:
            # 获取指数历史数据（最近1年+一些缓冲）
            end_date = datetime.now().strftime("%Y-%m-%d")
            start_date = (datetime.now() - timedelta(days=400)).strftime("%Y-%m-%d")
            
            # 从StockIndexService获取历史数据
            # 需要将代码格式转换，例如 000300.XSHG -> 000300
            symbol = code.split('.')[0]
            df = self.stock_index_service.get_history_data(
                codes=symbol,
                start_date=start_date,
                end_date=end_date
            )
            
            if df is None or df.empty:
                logger.warning(f"未获取到指数 {code} 的历史数据")
                return periods_data
            
            # 确保数据按日期排序
            df = df.sort_values('date').reset_index(drop=True)
            
            # 获取最新收盘价
            latest_close = df.iloc[-1]['close']
            
            # 定义各个时间段对应的数据点个数（交易日）
            periods_points = {
                '1d': 1,      # 1个交易日
                '1w': 5,      # 5个交易日（1周）
                '3w': 15,     # 15个交易日（3周）
                '1m': 22,     # 22个交易日（1个月）
                '2m': 44,     # 44个交易日（2个月）
                '3m': 66,     # 66个交易日（3个月）
                '1y': 244,    # 244个交易日（1年）
                'ytd': None   # 年初至今特殊处理
            }
            
            # 计算各个时间段的涨跌幅
            for period_name, points in periods_points.items():
                try:
                    if period_name == 'ytd':
                        # 年初至今：找到今年1月1日之后的第一个交易日
                        current_year = datetime.now().year
                        ytd_start = datetime(current_year, 1, 1)
                        ytd_df = df[df['date'] >= ytd_start]
                        if not ytd_df.empty:
                            start_close = ytd_df.iloc[0]['close']
                        else:
                            periods_data[period_name] = 0.0
                            continue
                    else:
                        # 基于数据点个数获取起始价格
                        if len(df) > points:
                            # 从最后一行往前数points个交易日
                            start_close = df.iloc[-(points + 1)]['close']
                        else:
                            # 如果数据不够指定天数，检查是否有足够数据计算
                            if len(df) >= 2:
                                # 使用所有可用数据的最早点
                                start_close = df.iloc[0]['close']
                                logger.debug(f"期间 {period_name}：需要 {points} 天，但只有 {len(df)} 天，使用最早数据")
                            else:
                                # 数据不足，跳过这个时间段
                                logger.debug(f"期间 {period_name}：只有 {len(df)} 天数据，无法计算")
                                periods_data[period_name] = 0.0
                                continue
                    
                    # 计算涨跌幅
                    if start_close > 0:
                        return_pct = ((latest_close - start_close) / start_close) * 100
                        periods_data[period_name] = return_pct
                    else:
                        periods_data[period_name] = 0.0
                        
                except (IndexError, KeyError) as e:
                    logger.warning(f"计算 {period_name} 涨跌幅时出错: {e}")
                    periods_data[period_name] = 0.0
            
        except Exception as e:
            logger.error(f"计算指数 {code} 涨跌幅失败: {e}")
            # 返回默认值
            for period in ['1d', '1w', '3w', '1m', '2m', '3m', '1y', 'ytd']:
                periods_data[period] = 0.0
        
        return periods_data

    def load_index_returns_only(self, days_limit: int = 230):
        """只加载指数收益率数据，不包含走势图数据（高效模式）"""
        logger.info("使用高效模式：只获取收益率数据，不包含走势图")
        return self.load_all_index_data(days_limit=days_limit, include_intraday_chart=False)

    def load_all_index_data(self, days_limit: int = 230, include_intraday_chart: bool = True):
        """加载所有指数数据 - 使用批量API调用优化
        
        Args:
            days_limit: 历史数据天数限制
            include_intraday_chart: 是否包含当天分钟级走势图数据
        """
        try:
            # 从数据库获取指数配置
            index_configs = self.get_index_configs()
            if not index_configs:
                logger.warning("未获取到指数配置数据")
                return []
            
            # 提取所有指数代码
            all_codes = [config[0] for config in index_configs]
            code_to_name = {config[0]: config[1] for config in index_configs}
            
            # 批量获取所有指数的历史数据
            # end_date = datetime.now().strftime('%Y-%m-%d')
            end_date = get_latest_trading_date()
            start_date = (datetime.now() - timedelta(days=days_limit)).strftime('%Y-%m-%d')
            
            logger.info(f"批量获取 {len(all_codes)} 个指数的日线历史数据（{days_limit}天），用于计算各时间段收益率")
            
            all_history_data = self.get_daily_history_with_cache(
                codes=all_codes,
                start_date=start_date,
                end_date=end_date
            )
            
            # 按指数代码分组数据
            grouped_data = {}
            if all_history_data is not None and not all_history_data.empty:
                for code, group in all_history_data.groupby('code'):
                    grouped_data[str(code)] = group.reset_index(drop=True)
            
            # 根据参数决定是否获取当天分钟数据
            grouped_minute_data = {}
            if include_intraday_chart:
                today_str = get_latest_trading_date()
                start_datetime = f"{today_str} 09:00:00"
                end_datetime = f"{today_str} 16:00:00"
                
                logger.info(f"批量获取 {len(all_codes)} 个指数的当天分钟数据用于走势图展示")
                all_minute_data = self.stock_index_service.get_index_minute_data(
                    codes=all_codes,
                    start_datetime=start_datetime,
                    end_datetime=end_datetime
                )
                
                # 按指数代码分组分钟数据
                if all_minute_data is not None and not all_minute_data.empty:
                    logger.info(f"成功获取 {len(all_minute_data)} 条分钟数据用于走势图")
                    for code, group in all_minute_data.groupby('code'):
                        chart_data = group.sort_values('datetime')['close'].tolist()
                        grouped_minute_data[str(code)] = chart_data
                else:
                    logger.warning("未获取到当天分钟数据")
            else:
                logger.info("跳过分钟数据获取，使用空走势图数据")
            
            # 准备表格数据
            table_data = []
            
            for config in index_configs:
                code = config[0]
                name = config[1]
                
                # 从批量数据中获取该指数的数据
                df = grouped_data.get(code)
                if df is not None and not df.empty:
                    periods_data = self._calculate_periods_return_from_df(df)
                    latest_close = df.iloc[-1]['close']  # 获取最新收盘价（来自日线数据）
                    
                    # 如果有当天分钟数据且需要更精确的当天涨跌幅，可以使用分钟数据的最新价格
                    if include_intraday_chart and code in grouped_minute_data:
                        minute_chart = grouped_minute_data[code]
                        if minute_chart:  # 如果有分钟数据
                            current_price = minute_chart[-1]  # 最新分钟价格
                            # 重新计算当天涨跌幅（1d）使用分钟级最新价格
                            if len(df) >= 2:  # 至少需要有昨天的数据
                                yesterday_close = df.iloc[-2]['close'] if len(df) > 1 else df.iloc[-1]['close']
                                if yesterday_close > 0:
                                    periods_data['1d'] = ((current_price - yesterday_close) / yesterday_close) * 100
                else:
                    logger.warning(f"未获取到指数 {code} 的历史数据")
                    periods_data = {period: 0.0 for period in ['1d', '1w', '3w', '1m', '2m', '3m', '1y', 'ytd']}
                    latest_close = 0.0
                
                # 获取该指数的走势图数据
                chart_data = grouped_minute_data.get(code, [])
                
                # 构造表格行数据，把走势图数据放在第3列，去掉最新价列
                row_data = [code, name, chart_data] + [
                    f"{periods_data.get(period, 0):.2f}%" 
                    for period in ['1d', '1w', '3w', '1m', '2m', '3m', '1y', 'ytd']
                ]
                table_data.append(row_data)
            
            logger.info(f"成功加载 {len(table_data)} 个指数数据")
            return table_data
            
        except Exception as e:
            logger.exception(f"加载指数数据时出错: {e}")
            return []

    def _calculate_periods_return_from_df(self, df):
        """从DataFrame计算各个时间段的涨跌幅"""
        periods_data = {}
        
        try:
            if df is None or df.empty:
                return {period: 0.0 for period in ['1d', '1w', '3w', '1m', '2m', '3m', '1y', 'ytd']}
            
            # 确保数据按日期排序
            df = df.sort_values('date').reset_index(drop=True)
            data_length = len(df)
            
            # 获取最新收盘价
            latest_close = df.iloc[-1]['close']
            logger.debug(f"收益率计算：共 {data_length} 天历史数据，最新价格 {latest_close}")
            
            # 定义各个时间段对应的数据点个数（交易日）
            periods_points = {
                '1d': 1,      # 1个交易日
                '1w': 5,      # 5个交易日（1周）
                '3w': 15,     # 15个交易日（3周）
                '1m': 22,     # 22个交易日（1个月）
                '2m': 44,     # 44个交易日（2个月）
                '3m': 66,     # 66个交易日（3个月）
                '1y': 244,    # 244个交易日（1年）
                'ytd': None   # 年初至今特殊处理
            }
            
            # 计算各个时间段的涨跌幅
            for period_name, points in periods_points.items():
                try:
                    if period_name == 'ytd':
                        # 年初至今：找到今年1月1日之后的第一个交易日
                        current_year = datetime.now().year
                        ytd_start = datetime(current_year, 1, 1)
                        ytd_df = df[df['date'] >= ytd_start]
                        if not ytd_df.empty:
                            start_close = ytd_df.iloc[0]['close']
                        else:
                            periods_data[period_name] = 0.0
                            continue
                    else:
                        # 基于数据点个数获取起始价格
                        if len(df) > points:
                            # 从最后一行往前数points个交易日
                            start_close = df.iloc[-(points + 1)]['close']
                        else:
                            # 如果数据不够指定天数，检查是否有足够数据计算
                            if len(df) >= 2:
                                # 使用所有可用数据的最早点
                                start_close = df.iloc[0]['close']
                                logger.debug(f"期间 {period_name}：需要 {points} 天，但只有 {len(df)} 天，使用最早数据")
                            else:
                                # 数据不足，跳过这个时间段
                                logger.debug(f"期间 {period_name}：只有 {len(df)} 天数据，无法计算")
                                periods_data[period_name] = 0.0
                                continue
                    
                    # 计算涨跌幅
                    if start_close > 0:
                        return_pct = ((latest_close - start_close) / start_close) * 100
                        periods_data[period_name] = return_pct
                    else:
                        periods_data[period_name] = 0.0
                        
                except (IndexError, KeyError) as e:
                    logger.warning(f"计算 {period_name} 涨跌幅时出错: {e}")
                    periods_data[period_name] = 0.0
            
        except Exception as e:
            logger.error(f"计算涨跌幅失败: {e}")
            # 返回默认值
            for period in ['1d', '1w', '3w', '1m', '2m', '3m', '1y', 'ytd']:
                periods_data[period] = 0.0
        
        return periods_data

    def load_hk_index_data(self, days_limit: int = 365):
        """加载港股指数数据"""
        try:
            # 计算需要的日期范围 - 获取1年的数据用于计算各个时间段，同时包含30天用于走势图
            end_date = datetime.now().strftime("%Y-%m-%d")
            start_date = (datetime.now() - timedelta(days=days_limit)).strftime("%Y-%m-%d")
            
            # 使用港股指数服务获取指定时间范围的数据
            result = self.hk_index_service.get_hk_index_history_data(
                start_date=start_date, 
                end_date=end_date
            )
            
            if result and result.get("code") == 200:
                data = result.get("data", [])
                
                # 按代码分组并计算涨跌幅数据
                table_data = []
                grouped_data = {}
                for item in data:
                    code = item.get("code", "")
                    if code not in grouped_data:
                        grouped_data[code] = []
                    grouped_data[code].append(item)
                
                # 为每个指数计算涨跌幅
                for code, items in grouped_data.items():
                    if not items:
                        continue
                        
                    # 按日期排序
                    items.sort(key=lambda x: x.get("date", ""))
                    latest_item = items[-1]
                    
                    # 计算各个时间段的涨跌幅
                    periods_data = self._calculate_periods_return_from_hk_data(items)
                    
                    # 获取近30天的收盘价数据用于走势图
                    thirty_days_ago = (datetime.now() - timedelta(days=30)).strftime("%Y-%m-%d")
                    chart_data = []
                    for item in items:
                        if item.get('date', '') >= thirty_days_ago:
                            chart_data.append(item.get('close', 0))
                    
                    # 构造表格行数据，使用服务中的名称获取方法
                    index_name = self.hk_index_service.get_hk_index_name(code)
                    row_data = [
                        code,
                        index_name,
                        chart_data,  # 近30天走势图数据
                        f"{periods_data.get('1d', 0):.2f}%",
                        f"{periods_data.get('1w', 0):.2f}%", 
                        f"{periods_data.get('3w', 0):.2f}%",
                        f"{periods_data.get('1m', 0):.2f}%",
                        f"{periods_data.get('2m', 0):.2f}%",
                        f"{periods_data.get('3m', 0):.2f}%",
                        f"{periods_data.get('1y', 0):.2f}%",
                        f"{periods_data.get('ytd', 0):.2f}%"
                    ]
                    table_data.append(row_data)
                
                return table_data
            else:
                return []
                
        except Exception as e:
            logger.exception(f"获取港股指数数据时出错: {e}")
            return []

    def load_global_index_data(self, days_limit: int = 365):
        """加载全球指数数据"""
        try:
            # 计算需要的日期范围 - 获取1年的数据用于计算各个时间段，同时包含30天用于走势图
            end_date = datetime.now().strftime("%Y-%m-%d")
            start_date = (datetime.now() - timedelta(days=days_limit)).strftime("%Y-%m-%d")
            
            # 使用全球指数服务获取指定时间范围的数据
            result = self.global_index_service.get_global_index_history_data(
                start_date=start_date, 
                end_date=end_date
            )
            
            if result and result.get("code") == 200:
                data = result.get("data", [])
                
                # 按代码分组并计算涨跌幅数据
                table_data = []
                grouped_data = {}
                for item in data:
                    code = item.get("code", "")
                    if code not in grouped_data:
                        grouped_data[code] = []
                    grouped_data[code].append(item)
                
                # 为每个指数计算涨跌幅
                for code, items in grouped_data.items():
                    if not items:
                        continue
                        
                    # 按日期排序
                    items.sort(key=lambda x: x.get("date", ""))
                    latest_item = items[-1]
                    
                    # 计算各个时间段的涨跌幅
                    periods_data = self._calculate_periods_return_from_global_data(items)
                    
                    # 获取近30天的收盘价数据用于走势图
                    thirty_days_ago = (datetime.now() - timedelta(days=30)).strftime("%Y-%m-%d")
                    chart_data = []
                    for item in items:
                        if item.get('date', '') >= thirty_days_ago:
                            chart_data.append(item.get('close', 0))
                    
                    # 构造表格行数据，使用服务中的名称获取方法
                    index_name = self.global_index_service.get_global_index_name(code)
                    row_data = [
                        code,
                        index_name,
                        chart_data,  # 近30天走势图数据
                        f"{periods_data.get('1d', 0):.2f}%",
                        f"{periods_data.get('1w', 0):.2f}%",
                        f"{periods_data.get('3w', 0):.2f}%",
                        f"{periods_data.get('1m', 0):.2f}%",
                        f"{periods_data.get('2m', 0):.2f}%",
                        f"{periods_data.get('3m', 0):.2f}%",
                        f"{periods_data.get('1y', 0):.2f}%",
                        f"{periods_data.get('ytd', 0):.2f}%"
                    ]
                    table_data.append(row_data)
                
                return table_data
            else:
                return []
                
        except Exception as e:
            logger.exception(f"获取全球指数数据时出错: {e}")
            return []

    def _calculate_periods_return_from_hk_data(self, items):
        """从港股数据计算各个时间段的涨跌幅"""
        periods_data = {}
        
        try:
            if not items:
                return {period: 0.0 for period in ['1d', '1w', '3w', '1m', '2m', '3m', '1y', 'ytd']}
            
            # 按日期排序
            items.sort(key=lambda x: x.get("date", ""))
            
            # 获取最新收盘价
            latest_close = items[-1].get('close', 0)
            
            # 定义各个时间段对应的数据点个数（交易日）
            periods_points = {
                '1d': 1,      # 1个交易日
                '1w': 5,      # 5个交易日（1周）
                '3w': 15,     # 15个交易日（3周）
                '1m': 22,     # 22个交易日（1个月）
                '2m': 44,     # 44个交易日（2个月）
                '3m': 66,     # 66个交易日（3个月）
                '1y': 244,    # 244个交易日（1年）
                'ytd': None   # 年初至今特殊处理
            }
            
            # 计算各个时间段的涨跌幅
            for period_name, points in periods_points.items():
                try:
                    if period_name == 'ytd':
                        # 年初至今：找到今年1月1日之后的第一个交易日
                        current_year = datetime.now().year
                        ytd_start_str = f"{current_year}-01-01"
                        
                        ytd_items = [item for item in items if item.get('date', '') >= ytd_start_str]
                        if ytd_items:
                            start_close = ytd_items[0].get('close', 0)
                        else:
                            periods_data[period_name] = 0.0
                            continue
                    else:
                        # 基于数据点个数获取起始价格
                        if len(items) > points:
                            # 从最后一行往前数points个交易日
                            start_close = items[-(points + 1)].get('close', 0)
                        else:
                            # 如果数据不够，使用最早的数据
                            start_close = items[0].get('close', 0)
                    
                    # 计算涨跌幅
                    if start_close > 0 and latest_close > 0:
                        return_pct = ((latest_close - start_close) / start_close) * 100
                        periods_data[period_name] = return_pct
                    else:
                        periods_data[period_name] = 0.0
                        
                except (IndexError, KeyError) as e:
                    logger.warning(f"计算港股 {period_name} 涨跌幅时出错: {e}")
                    periods_data[period_name] = 0.0
            
        except Exception as e:
            logger.error(f"计算港股涨跌幅失败: {e}")
            # 返回默认值
            for period in ['1d', '1w', '3w', '1m', '2m', '3m', '1y', 'ytd']:
                periods_data[period] = 0.0
        
        return periods_data

    def _calculate_periods_return_from_global_data(self, items):
        """从全球指数数据计算各个时间段的涨跌幅"""
        periods_data = {}
        
        try:
            if not items:
                return {period: 0.0 for period in ['1d', '1w', '3w', '1m', '2m', '3m', '1y', 'ytd']}
            
            # 按日期排序
            items.sort(key=lambda x: x.get("date", ""))
            
            # 获取最新收盘价
            latest_close = items[-1].get('close', 0)
            
            # 定义各个时间段对应的数据点个数（交易日）
            periods_points = {
                '1d': 1,      # 1个交易日
                '1w': 5,      # 5个交易日（1周）
                '3w': 15,     # 15个交易日（3周）
                '1m': 22,     # 22个交易日（1个月）
                '2m': 44,     # 44个交易日（2个月）
                '3m': 66,     # 66个交易日（3个月）
                '1y': 244,    # 244个交易日（1年）
                'ytd': None   # 年初至今特殊处理
            }
            
            # 计算各个时间段的涨跌幅
            for period_name, points in periods_points.items():
                try:
                    if period_name == 'ytd':
                        # 年初至今：找到今年1月1日之后的第一个交易日
                        current_year = datetime.now().year
                        ytd_start_str = f"{current_year}-01-01"
                        
                        ytd_items = [item for item in items if item.get('date', '') >= ytd_start_str]
                        if ytd_items:
                            start_close = ytd_items[0].get('close', 0)
                        else:
                            periods_data[period_name] = 0.0
                            continue
                    else:
                        # 基于数据点个数获取起始价格
                        if len(items) > points:
                            # 从最后一行往前数points个交易日
                            start_close = items[-(points + 1)].get('close', 0)
                        else:
                            # 如果数据不够，使用最早的数据
                            start_close = items[0].get('close', 0)
                    
                    # 计算涨跌幅
                    if start_close > 0 and latest_close > 0:
                        return_pct = ((latest_close - start_close) / start_close) * 100
                        periods_data[period_name] = return_pct
                    else:
                        periods_data[period_name] = 0.0
                        
                except (IndexError, KeyError) as e:
                    logger.warning(f"计算全球指数 {period_name} 涨跌幅时出错: {e}")
                    periods_data[period_name] = 0.0
            
        except Exception as e:
            logger.error(f"计算全球指数涨跌幅失败: {e}")
            # 返回默认值
            for period in ['1d', '1w', '3w', '1m', '2m', '3m', '1y', 'ytd']:
                periods_data[period] = 0.0
        
        return periods_data

    def get_options_skew_data(self, underlying_symbol: str, target_date: str):
        """获取期权波动率偏斜数据"""
        return self.options_service.get_options_skew_data(underlying_symbol, target_date)

    def get_options_atm_historical_iv(self, underlying_symbol: str, start_date: str, end_date: str, option_class: str):
        """获取期权平值历史隐含波动率"""
        return self.options_service.get_options_atm_historical_iv(underlying_symbol, start_date, end_date, option_class)

    def get_iv_history(self, underlying_symbol: str, start_date: str, end_date: str):
        """获取期权隐含波动率历史数据"""
        return self.options_service.get_iv_history(underlying_symbol, start_date, end_date)

    def get_options_cone_data(self, underlying_symbol: str, target_date: str):
        """获取期权波动率锥数据"""
        return self.options_service.get_options_cone_data(underlying_symbol, target_date)

    def get_options_iv_surface_data(self, underlying_symbol: str, date: str, option_class: str):
        """获取期权隐含波动率3D曲面数据"""
        return self.options_service.get_options_iv_surface_data(underlying_symbol, date, option_class)
